﻿using HtmlAgilityPack;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using Trauberman.SOChat.StarLeaderboard.Domain;

namespace Trauberman.SOChat.StarLeaderboard.Services
{
	public class StarsRequest
	{
		private IUserService _userService;
		private IStarService _starService;
		public StarsRequest(IUserService userService, IStarService starService)
		{
			// Dependency Injection - These values will be injected at run time.
			_userService = userService;
			_starService = starService;
		}

		private const string URL_PATTERN = "http://chat.stackoverflow.com/rooms/info/{0}?tab=stars";
		public void LoadStars(int chatRoomId)
		{
			int count = 0;
			IEnumerable<StarredMessage> retreivedMessages;

			int page = 1;
			do
			{
				// loop through each star page and scrape stars
				// each page has 50 stars, so assume there is a next page if 
				// the current page has 50 stars on it.

				Console.WriteLine("  {0} - Loading Page {1} ", chatRoomId, page);

				retreivedMessages = LoadStarsPage(chatRoomId, page);

				_starService.InsertOrUpdate(retreivedMessages.ToArray());

				count += retreivedMessages.Count();

				page++;
			} while (retreivedMessages.Count() == 50);
			Console.WriteLine("  {0} - Total Starred Messages: {1}", chatRoomId, count);
		}

		private IEnumerable<StarredMessage> LoadStarsPage(int chatRoomId, int page = 1)
		{
			// append the page number for pages 2+
			string url = string.Format(URL_PATTERN, chatRoomId);
				url = url + "&page=" + page;

			HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
			request.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip");
			request.AutomaticDecompression = DecompressionMethods.GZip;
			request.UserAgent = "StackOverflow Star Leaderboard Scraper (http://starleaderboard.azurewebsites.net) by Kyle Trauberman (ktrauberman@gmail.com)";

			HtmlDocument doc = new HtmlDocument();
			// download the page and get the response stream.
			using (WebResponse response = request.GetResponse())
			using (Stream responseStream = response.GetResponseStream())
			{
				// load the stream in to HtmlAgilityPack
				doc.Load(responseStream);
			}

				// find the root node of all the stars on the page
				var contentNode = doc.GetElementbyId("content");

				// each star entry is in a node with a class of 'monologue'
				List<HtmlNode> monologueNodes = contentNode.SelectNodes("//div[contains(@class, 'monologue')]").ToList();

				List<StarredMessage> messages = new List<StarredMessage>();

				foreach (HtmlNode node in monologueNodes)
				{
					// for each starred monologue, scrape the necessary information
					HtmlNode messageNode = node
						.ChildNodes.Single(c => c.GetAttributeValue("class", string.Empty) == "messages")
						.ChildNodes.Single(c => c.GetAttributeValue("class", string.Empty).Contains("message"));

					// the text of the message
					HtmlNode messageContentNode = messageNode
						.ChildNodes.Single(c => c.GetAttributeValue("class", string.Empty) == "content");

					// the username of the person that said the message
					HtmlNode usernameNode = node
						.ChildNodes.Single(c => c.GetAttributeValue("class", string.Empty) == "signature")
						.ChildNodes.Single(c => c.GetAttributeValue("class", string.Empty) == "tiny-signature")
						.ChildNodes.Single(c => c.GetAttributeValue("class", string.Empty) == "username")
						.ChildNodes.Single(c => c.Name == "a");

					// the number of times the message was starred
				HtmlNode starCountNode = messageNode
						.ChildNodes.Single(c => c.GetAttributeValue("class", string.Empty) == "flash")
						.ChildNodes.Single(c => c.GetAttributeValue("class", string.Empty).Contains("stars"))
						.ChildNodes.Single(c => c.GetAttributeValue("class", string.Empty) == "times");

					// the StackOverflow message ID
					string messageIdAttributeValue = messageNode.GetAttributeValue("id", string.Empty);

					// the StackOverflow user ID
					string userIdAttributeValue = node.GetAttributeValue("class", string.Empty);

					if (!string.IsNullOrEmpty(messageIdAttributeValue))
					{
						// if we can parse the id of this message, build the StarredMessage object
						int messageId;
						if (int.TryParse(messageIdAttributeValue.Split('-')[1], out messageId))
						{
							StarredMessage msg = new StarredMessage();
							msg.SOid = messageId;
							msg.ChatRoom = chatRoomId;
							msg.MessageText = messageContentNode.InnerHtml;

							int userId;
							if (int.TryParse(userIdAttributeValue.Split('-')[1], out userId))
							{
								// if the user exists in our database already, reuse that user, otherwise, insert the user
								User user = _userService.GetUser(userId);
								if (user != null)
								{
									msg.User = user;
								}
								else
								{
									string userName = usernameNode.InnerText;
									msg.User = _userService.AddUser(userId, userName);
								}
							}

							// if the number of stars is missing, use 1, otherwise use the number present
							int starCount;
							if (int.TryParse(starCountNode.InnerText, out starCount))
							{
								msg.StarCount = starCount;
							}
							else
							{
								msg.StarCount = 1;
							}

							messages.Add(msg);
						}
						else
						{
							continue;
						}
					}
				}

				return messages;
			}
		}
	}
